portal: modern audit UI, native dark-mode OpenAPI viewer, sticky detail panel#7
Merged
Conversation
Audit: the event list column visibly resized whenever the right detail pane grew (long JSON header values pushed the proportional 2fr_3fr grid to give the right column more space). Replaced with a fixed left column and a min-w-0 right cell: grid-cols-1 lg:grid-cols-[420px_minmax(0,1fr)] minmax(0,1fr) is the load-bearing token — without the 0 floor, grid children inherit min-width: auto and the right column fights for space. Inner table now uses table-fixed with an explicit colgroup so the columns honor declared widths even when the path contains long tokens. EventDetail gained min-w-0 / truncate discipline so long Method+Path headers don't blow out the right pane. Discovery: dropped the /docs (Redoc) iframe. Redoc inside the iframe renders light-only and is unreadable against the portal's dark shell; piping a theme into the build-time Redoc HTML is heavier than rendering the OpenAPI document natively. Replaced with a native OpenAPI 3.1 reference view that fetches /openapi.json and renders groups, operations (collapsible cards), parameters, request body schema, responses, and per-media-type examples — all against the portal's CSS variables, so light and dark reach full parity. Includes a tag rail, search/filter, and per-block clipboard copy. /docs is still served and is linked from the page header for the canonical Redoc view. Accessibility / robustness: - aria-expanded on every disclosure button (operation cards, response rows, tag rail uses aria-pressed for filter state). - Tag rail and operation header use min-w-0 + truncate so long names and summaries don't push out the rest of the row. - Response rows with no body render a non-interactive div so keyboard users don't tab through a button that does nothing. - CodeBlock copy-feedback timer is cleared on unmount (useRef + useEffect) so React strict-mode double-mount doesn't leave a timer firing on a dead component. - Discovery fetch uses AbortController so a fast unmount cancels the in-flight request. - Loading / missing / error states show appropriate subtitles and hide the JSON/YAML/Redoc download links (they 404 together with /openapi.json — offering them when missing is just dead-link theater). Docs: portal.md, http-api.md, and overview.md all advertised Discovery as a Redoc/Swagger UI iframe — updated to describe the native renderer and /docs as a sibling link.
Two coupled changes to fix the "click row at bottom, scroll back to top to see detail" UX failure on the Audit page: ui/src/App.tsx Outer layout was `grid ... min-h-full` with `<main className="p-6 overflow-auto">`. min-h-full means the grid grows with content so the *document* scrolls and main never overflows — `overflow-auto` is a no-op, and any `position: sticky` inside main has no scroll context to anchor to. Switched the outer grid to `h-full` so main is height-pinned to the viewport, its overflow-auto actually fires, and sticky works. Added `overflow-y-auto` to the sidebar so it scrolls independently if its content ever exceeds the viewport. Verified Dashboard, Endpoints, and Discovery still render (Discovery's `h-[calc(100vh-3rem)]` shell continues to fit because main is now exactly viewport-height). ui/src/pages/Audit.tsx Right detail column gets `lg:sticky lg:top-6 lg:self-start lg:max-h-[calc(100vh-3rem)] lg:overflow-y-auto`. self-start so the right grid track doesn't stretch to the left track's height (sticky needs room within its containing block to slide). max-h + overflow-y-auto so a tall detail (multiple JSON payload blocks) scrolls within itself instead of pushing the sticky panel off screen. Confirmed via getBoundingClientRect after scrolling to the 100th row: detail panel pins at top: 48px, fully in viewport. Also widened Method column from 72px → 88px (DELETE pill rendered ~78px; the 72px column let it overflow into Path by ~7px). Left pane widened 440px → 460px to keep the Path column at ~226px usable width.
The portal UI rewrite (audit pills, sticky detail, native Discovery viewer) made every committed portal screenshot stale. The homepage carousel and operations/portal.md showed the old design. Changes: - scripts/screenshots/screenshots.mjs: add Discovery to the capture list. Pre-expands the first operation card so the screenshot shows what the page actually does (parameters table, response schema) instead of a closed-card list. - docs/images/portal/: regenerated all 18 PNGs (light + dark for every page) plus 2 new ones for Discovery. Captured against a binary built with `make build VERSION=v1.0.0` so the version badge reads "v1.0.0" rather than the git-describe "v1.0.0-dirty" during in-progress work. - docs/operations/portal.md: new Discovery section between Endpoints and Audit log, with light/dark screenshot embeds. - docs/overrides/home.html: added Discovery to the carousel; updated the Endpoint Detail caption (Try-It panel is shipped, not "arrives with"). - ui/src/pages/Discovery.tsx: heading rendered "vv1.0.0" because the span unconditionally prepended "v" to an info.version that already started with "v". Strip a leading "v" before re-adding.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Portal UI overhaul focused on three user-reported defects in the audit and discovery pages, plus the documentation that ships alongside them.
/docs(Redoc) remains served and is linked from the page header for operators who prefer it.operations/portal.mdgains a Discovery section, the homepage carousel gets a Discovery slide and an updated Endpoints caption.Defect → fix mapping
grid-cols-[2fr_3fr]plus missingmin-w-0let long JSON values steal width from the listgrid-cols-1 lg:grid-cols-[460px_minmax(0,1fr)]+table-fixed+ explicit<colgroup>widthstoLocaleTimeString()produces11+chars in en-US; the 88px Time col and 64px Method col couldn't hold itHH:MM:SSformatter; widths re-measured viagetBoundingClientRect(76 / 88 / Path / 64)<main className=\"... overflow-auto\">inside amin-h-fullouter grid never overflowed, so the document scrolled andposition: stickyhad no scroll contexth-fullsomainbecomes the real scroll container; detail panel useslg:sticky lg:top-6 lg:self-startwith its ownmax-h + overflow-y-autofor tall payload bodiesvv1.0.0vtoinfo.version, which already starts withvvbefore re-addingscreenshots.mjs,portal.md, and the homepage carousel; all 18 existing PNGs regenerated, 2 new Discovery PNGs addedAccessibility / robustness picked up along the way
aria-expandedon every disclosure (operation cards, response rows).<div>so keyboard users don't Tab through dead buttons.aria-pressedfor filter state and propermin-w-0so long tag names don't push the count badge off-screen.CodeBlockcopy-feedback timer is cleaned up on unmount; the Discovery fetch usesAbortController./openapi.json).Files touched
ui/src/App.tsx— outer gridmin-h-full→h-fullsomainowns the scroll context; sidebar gainsoverflow-y-auto.ui/src/pages/Audit.tsx— fixed-width left column,table-fixedcolgroup, 24-hour time,MethodPill/StatusPill, sticky detail panel.ui/src/pages/Discovery.tsx— full native OpenAPI viewer replacing the iframe (~760 lines).scripts/screenshots/screenshots.mjs— capture Discovery (pre-expanded operation card).docs/operations/portal.md— Discovery section + embeds.docs/overrides/home.html— carousel adds Discovery; Endpoints caption updated.docs/reference/http-api.md—/docsdescription acknowledges the native portal renderer.docs/images/portal/*.png— all regenerated;discovery-{light,dark}.pngadded.Verification
Each commit went through
make verify(green) plus a live-browser pass before commit:mcp__claude-in-chrome.getBoundingClientRecton every table cell across 20 rows and assertedoverflows: [](first pass found aDELETEpill overflow; widened Method column 72px → 88px; re-measured to zero).Test plan
/portal/auditin a real browser, scroll the list, click rows at the top and bottom — detail panel stays visible./portal/discovery, expand operation cards, use the search bar, switch tags in the left rail; check that the Redoc link in the header opens/docsin a new tab./openapi.jsonis unavailable (e.g.oapiDoc=nilbuild), Discovery's loading → missing transition does not render dead header links.make screenshotsagainst a cleanmainbuild reproduces every PNG in this PR.